/*
 * iTunes Movie Trailer Import
 * Copyright (c) 2005 Jonathan Bradshaw
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
 * of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
using System.Web;
using MediaPortal.GUI.Library;

namespace ITMSClient
{
	/// <summary>
	/// Provides the heavy lifting for interfacing with the iTunes server.
	/// </summary>
	public sealed class ITMSHelper
	{
		#region Constants
		// The iTunes server won't talk to us unless we pretend to be a bona-fide iTunes client
		private const string USER_AGENT = "User-Agent: iTunes/6.0 (Windows)";
		//private static Hashtable HighDef = HDHelper.GetAvailableMovies();
		#endregion
		
		
		static public TrailerInfo PopulateTrailerUrls(TrailerInfo trailerInfo)
		{
			MatchCollection matches;
			//Console.WriteLine("");
			ITMSContent infoPage = GetITMSPage(trailerInfo.Url);
			if (infoPage.IsValid)
			{
				Match field = RegexParsers.moviedetailsRegex1.Match(infoPage.Xml);
				//String desc;
				if (field.Success)
				{
					Log.Info("got the movie overview  {0}",trailerInfo.Title);
					trailerInfo.Overview = field.Groups["overview"].Value.Replace("\r\n", " ").Replace('\u0092', '\'').Replace('\u0093', '"').Replace('\u0094', '"');
					//Log.Info("got the movie overview  {0}", trailerInfo.Overview);
				}
				field = RegexParsers.moviedetailsRegex2.Match(infoPage.Xml);
				if(field.Success){
					Log.Info("movie details 2 regex successful");
					trailerInfo.Release = field.Groups["release"].Value.Replace("\r\n"," ");
					trailerInfo.Genre = field.Groups["genre"].Value;
				}
				else{
					Log.Info("movie details 2 regex unsuccessful");
				}
				String name;
				String url;
				
				Dictionary<string, string> trailers = new Dictionary<string, string>();
				// First regular expression parser
				matches = RegexParsers.trailerLinkRegex.Matches(infoPage.Xml);
				if (matches.Count>0)
				{
					foreach(Match fields in matches){
						name = fields.Groups["name"].Value;
						url=fields.Groups["url"].Value;
						trailers.Add(name,url);
						
					}
					trailerInfo.trailers = trailers;
				}
			}
			return trailerInfo;
		}
		#region Public Methods
		/// <summary>
		/// Populates the full meta data for a particular trailer
		/// </summary>
		/// <param name="trailerInfo"></param>
		/// <returns></returns>
		static public TrailerInfo PopulateTrailerDetails(TrailerInfo trailerInfo, ITMSClient.TrailerSize desiredSize)
		{
			Log.Info("pop details for " + trailerInfo.Url);
			//Log.Info("Populating Trailer details for {0}", trailerInfo.Title);
			string[] MONTHS = new string[]{"January","February","March","April","May","June","July","August","September","October","November","December"};
			//const string DIRECTOR = "(dir.)";
			//Match fields;
			StringBuilder genres = new StringBuilder("|");
			StringBuilder actors = new StringBuilder("|");

			ITMSContent infoPage = GetITMSPage(trailerInfo.Url);
			//Log.Info("got itms page for " + trailerInfo.Url);
			//Log.Info(infoPage.Xml);
			if (infoPage.IsValid)
			{
				
				trailerInfo.TrailerStreamCollection = new TrailerStreamCollection();
				// Get movie trailer streams
				if (desiredSize != TrailerSize.None && trailerInfo.TrailerStreamCollection.Count == 0)
				{
					//foreach (Capture url in fields.Groups["urls"].Captures)
					//{
					//Log.Info("Found stream url:{0}", url);
					//TrailerStreamCollection tsc = GetTrailerStreamCollection(new Uri(infoPage.WebResponse.ResponseUri, url.Value));
					TrailerStreamCollection tsc = GetTrailerStreamCollection(infoPage);
					
					TrailerStream myStream = GetDesiredTrailerStream(tsc, desiredSize);
					Log.Info("Adding stream to collection:" + myStream.Uri);
					trailerInfo.TrailerStreamCollection.Add(myStream);
					//trailerInfo.TrailerStreamCollection = tsc;
					//}
				}
				trailerInfo.TrailerStreamCollection.TrimToSize();
				
				//}
			}
			else
			{
				Log.Debug("Invalid page {0} for trailer {1}", trailerInfo.Url, trailerInfo.Title);
			}
			return trailerInfo;
		}
		
		/// <summary>
		/// Downloads the cover image to the specified destination directory if
		/// the file does not already exist. The filename is taken from the
		/// original image URL.
		/// </summary>
		/// <param name="destinationDirectory">Destination directory.</param>
		/// <returns></returns>
		static public string DownloadPosterImage(TrailerInfo TrailerInfo, string destinationDirectory)
		{
			WebClient webClient = new WebClient();
			Uri uri = TrailerInfo.PosterUrl;
			string filename = Path.Combine(destinationDirectory, uri.AbsolutePath.Remove(0, uri.AbsolutePath.LastIndexOf('/')+1));
			if (File.Exists(filename) == false)
				try
			{
				webClient.DownloadFile(uri.AbsoluteUri, filename);
			}
			catch (WebException)
			{
				return String.Empty;
			}
			return filename;
		}

		/// <summary>
		/// Gets the itms page.
		/// </summary>
		/// <param name="uri">URI.</param>
		/// <returns>ITMSContent</returns>
		static public ITMSContent GetITMSPage(Uri uri)
		{
			return GetITMSPage(uri, DateTime.MinValue);
		}

		static public ITMSContent GetITMSPage(Uri uri, DateTime ifModifiedSince)
		{
			//Log.Info("GetItmsPage witrh uri:" + uri);
			const int TIMEOUT = 30000; // 30 seconds
			StreamReader     sReader  = null;
			HttpWebRequest   request  = null;
			HttpWebResponse  response = null;
			ITMSContent	     content  = null;

			//Log.Debug("GetITMSPage: " + uri.AbsoluteUri);
			//Log.Info("GetITMSPage: " + uri.AbsoluteUri);
			try
			{
				request = (HttpWebRequest) HttpWebRequest.Create(uri);
				request.KeepAlive = false;
				request.Pipelined = false;
				request.Timeout = TIMEOUT; // Timeout for initial connection
				request.ReadWriteTimeout = TIMEOUT; // Timeout during connection
				request.UserAgent = USER_AGENT;

				if (ifModifiedSince != DateTime.MinValue)
					request.IfModifiedSince = ifModifiedSince;
				//Log.Info("getiing request response");
				response = (HttpWebResponse) request.GetResponse();
				//Log.Info("got response");
				sReader = new StreamReader(response.GetResponseStream(),
				                           System.Text.Encoding.UTF8, false, (int) Math.Abs(response.ContentLength));
				//Log.Info("Got Response Stream");
				
				//Log.Info("Download xml");
				content = new ITMSContent(response,
				                          HttpUtility.HtmlDecode(
				                          	sReader.ReadToEnd()));
				//Log.Info("created ITMSContent");
				//Log.Debug(response.LastModified);
			}
			catch (WebException ex)
			{
				Log.Debug(ex.GetBaseException().Message);
				content = new ITMSContent(ex);
				if (ex.Response is HttpWebResponse)
				{
					HttpWebResponse httpResponse = ex.Response as HttpWebResponse;
					if (httpResponse.StatusCode == HttpStatusCode.NotModified)
					{
						Log.Debug(uri.AbsoluteUri + " not modified since " + ifModifiedSince);
						content = new ITMSContent(response, String.Empty);
					}
				}
			}catch(Exception e){
				Log.Error(e);
			}
			finally
			{
				if(sReader != null)
					sReader.Close();
				if(response != null)
					response.Close();
			}
			return content;
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Gets the trailer stream.
		/// </summary>
		/// <param name="uri">URI.</param>
		/// <returns></returns>
		private static TrailerStream GetDesiredTrailerStream(TrailerStreamCollection trailerStreams, TrailerSize desiredSize)
		{
			Log.Info("GetDesiredTrailerStream - desiredSize=" + desiredSize);
			foreach (TrailerStream stream1 in trailerStreams)
			{
				Log.Info("GetDesiredTrailerStream - size available:" + stream1.Size);
			}
			//Log.Info("getting desired Trailer size of {0}", desiredSize);
			// Find the largest desired size supported by the available streams
			while (trailerStreams.ContainsKey(desiredSize) == false && desiredSize > 0)
				desiredSize = desiredSize - 1;

			TrailerStream stream = trailerStreams[desiredSize];
			
			
			return stream;
		}

		/// <summary>
		/// Gets the trailer stream collection.
		/// </summary>
		/// <param name="trailerInfo">Trailer info.</param>
		/// <returns></returns>
		//private static TrailerStreamCollection GetTrailerStreamCollection(Uri url)
		private static TrailerStreamCollection GetTrailerStreamCollection(ITMSContent trailerPage)
		{
			//Log.Info("GetTrailerStreamCollection url:" + url);
			TrailerStreamCollection streamCollection = new TrailerStreamCollection(3);

			//ITMSContent trailerPage = GetITMSPage(url);
			if (trailerPage.IsValid)
			{
				string type = String.Empty;
				TimeSpan length = TimeSpan.Zero;
				Match streamDetails = RegexParsers.streamDetailsRegex.Match(trailerPage.Xml);
				//Debug.Assert(streamDetails.Success);

				// Details not available for older trailers
				if (streamDetails.Groups["type"].Success)
					type = streamDetails.Groups["type"].Value;
				if (streamDetails.Groups["minutes"].Success)
				{
					string minutes = streamDetails.Groups["minutes"].Value;
					if (String.IsNullOrEmpty(minutes))
						minutes = "0";
					length = new TimeSpan(0, Convert.ToInt32(minutes), Convert.ToInt32(streamDetails.Groups["seconds"].Value));
				}
				MatchCollection streamMatches = RegexParsers.streamRegex.Matches(trailerPage.Xml);
				foreach (Match m in streamMatches)
				{
					TrailerSize size = (TrailerSize) Enum.Parse(typeof(TrailerSize), m.Groups["size"].Value, true);

					//Log.Info("found size {0}",size);
					Uri uri = new Uri(trailerPage.WebResponse.ResponseUri, m.Groups["url"].Value);
					//Log.Info("found url {0}", uri.ToString());
					TrailerStream ts = new TrailerStream(size, uri);
					ts.Length = length;
					ts.Type = type;
					streamCollection.Add(ts);
				}
			}
			return streamCollection;
		}
		#endregion
	}

}
